﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;

namespace theeg.DependencyAnalysis.CommonAddin.Behaviors
{
    public class History : DependencyObject
    {
        public event EventHandler CurrentItemChanged;

        private readonly object _lockHistory = new object();

        private readonly LinkedList<object> _historyStack = new LinkedList<object>();
        private readonly Stack _futureStack = new Stack();
        private bool _skipAddingToHistory;
        private int _size = 100;

        private readonly RelayCommand _backCommand;
        public RelayCommand BackCommand { get { return _backCommand; } }

        private readonly RelayCommand _foreCommand;
        public RelayCommand ForeCommand { get { return _foreCommand; } }

        public int Size
        {
            get { return _size; }
            set
            {
                lock (_lockHistory)
                {
                    _historyStack.Clear();
                    _futureStack.Clear();
                }
                _size = value;
            }
        }

        public object CurrentItem
        {
            get { return GetValue(CurrentItemProperty); }
            set { SetValue(CurrentItemProperty, value); }
        }

        public static readonly DependencyProperty CurrentItemProperty =
            DependencyProperty.Register("CurrentItem", typeof(object), typeof(History), new PropertyMetadata(OnCurrentItemChanged));

        private static void OnCurrentItemChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue == e.OldValue) return;
            var history = (History)dependencyObject;
            history.AddToHistory(e.NewValue);
            var eventHandler = history.CurrentItemChanged;
            if (eventHandler != null)
            {
                eventHandler(history, new EventArgs());
            }
        }

        private void AddToHistory(object value)
        {
            lock (_lockHistory)
            {
                if (_skipAddingToHistory) return;
                if (_historyStack.Count >= _size && _size > 0)
                {
                    _historyStack.RemoveFirst();
                }
                _historyStack.AddLast(value);
                _futureStack.Clear();
            }
        }

        public History()
        {
            _backCommand = new RelayCommand(x => BackExecuted(), x => BackCanExecute());
            _foreCommand = new RelayCommand(x => ForeExecuted(), x => ForeCanExecute());
        }

        public void BackExecuted()
        {
            lock (_lockHistory)
            {
                if (_historyStack.Count == 0) return;
                var pop = _historyStack.Last.Value;
                _historyStack.RemoveLast();
                _futureStack.Push(pop);
                _skipAddingToHistory = true;
                CurrentItem = _historyStack.Count == 0 ? null : _historyStack.Last.Value;
                _skipAddingToHistory = false;
            }
        }

        public bool BackCanExecute()
        {
            lock (_lockHistory)
            {
                return _historyStack.Count > 0;
            }
        }

        public void ForeExecuted()
        {
            lock (_lockHistory)
            {
                if (_futureStack.Count == 0) return;
                var pop = _futureStack.Pop();
                _historyStack.AddLast(pop);
                _skipAddingToHistory = true;
                CurrentItem = _historyStack.Count == 0 ? null : _historyStack.Last.Value;
                _skipAddingToHistory = false;
            }
        }

        public bool ForeCanExecute()
        {
            lock (_lockHistory)
            {
                return _futureStack.Count > 0;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
